package it.uniroma2.art.owlart.utilities.transform;

import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.model.ARTLiteral;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.model.NodeFilters;
import it.uniroma2.art.owlart.model.impl.ARTNodeFactoryImpl;
import it.uniroma2.art.owlart.models.OWLModel;
import it.uniroma2.art.owlart.models.SKOSModel;
import it.uniroma2.art.owlart.navigation.ARTLiteralIterator;
import it.uniroma2.art.owlart.navigation.ARTURIResourceIterator;
import it.uniroma2.art.owlart.vocabulary.OWL;
import it.uniroma2.art.owlart.vocabulary.RDFS;
import it.uniroma2.art.owlart.vocabulary.XmlSchema;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Simple utility class for transforming a SKOS thesaurus into an OWL ontology using the rules adopted by the
 * OAEI. <br />
 *  
 * @author manuel
 *
 */
public class SKOS2OWLConverter {
	public static abstract class URITransformer {
		private ARTNodeFactoryImpl nodeFact = new ARTNodeFactoryImpl();

		public abstract String transform(String source);
		public ARTURIResource transform(ARTURIResource source) {
			String newURI = transform(source.getURI());
			return nodeFact.createURIResource(newURI);
		}
	}

	private URITransformer uriTransformer = new URITransformer() {
		
		public String transform(String source) {
			return source;
		}

	};
	
	public void setURITransformer(URITransformer uriTransformer) {
		this.uriTransformer = uriTransformer;
	}
	
	public void doConversion(SKOSModel sourceModel, OWLModel targetModel) throws ModelAccessException, ModelUpdateException {
		try {
			targetModel.addInstance(targetModel.getBaseURI(), OWL.Res.ONTOLOGY, NodeFilters.MAINGRAPH);
			
			targetModel.addAnnotationProperty(RDFS.LABEL, null, NodeFilters.MAINGRAPH);
			targetModel.addAnnotationProperty(RDFS.COMMENT, null, NodeFilters.MAINGRAPH);
			targetModel.addAnnotationProperty(RDFS.SEEALSO, null , NodeFilters.MAINGRAPH);
			
			int count = 0;
			ARTURIResourceIterator it = sourceModel.listConcepts(true);
			try {
				while (it.streamOpen()) {
					ARTURIResource concept = it.getNext();

					transformConcept(sourceModel, targetModel, concept);
					
					count++;
					if (count % 10 == 0) System.err.println("Processed Concepts = " + count);					
				}
			} finally {
				it.close();
			}
			
			System.err.println("Count = " + count);
		} finally {
			sourceModel.close();
		}
	}

	private void transformConcept(SKOSModel skosModel, OWLModel owlModel, ARTURIResource concept) throws ModelUpdateException, ModelAccessException {
		owlModel.addClass(transformURI(concept).getURI(), NodeFilters.MAINGRAPH);
		
		transformBroaders(skosModel, owlModel, concept);
		transformRelated(skosModel, owlModel, concept);
		
		transformPrefLabels(skosModel, owlModel, concept);
		transformAltLabels(skosModel, owlModel, concept);
		transformScopeNotes(skosModel, owlModel, concept);
		transformNotations(skosModel, owlModel, concept);
		transformDefinitions(skosModel, owlModel, concept);
	}

	private void transformDefinitions(SKOSModel skosModel, OWLModel owlModel,
			ARTURIResource concept) throws ModelAccessException, ModelUpdateException {
		ARTURIResource currentClass = transformURI(concept);
		ARTLiteralIterator it = skosModel.listDefinitions(concept, true);
		try {
			while (it.streamOpen()) {
				ARTLiteral label = it.getNext();
				owlModel.addComment(currentClass, label.getLabel(), label.getLanguage(), NodeFilters.MAINGRAPH);
			}
		} finally {
			it.close();
		}		
	}

	private void transformScopeNotes(SKOSModel skosModel, OWLModel owlModel, ARTURIResource concept) throws ModelUpdateException, ModelAccessException {
		ARTURIResource currentClass = transformURI(concept);
		ARTLiteralIterator it = skosModel.listScopeNotes(concept, true);
		try {
			while (it.streamOpen()) {
				ARTLiteral label = it.getNext();
				owlModel.addComment(currentClass, label.getLabel(), label.getLanguage(), NodeFilters.MAINGRAPH);
			}
		} finally {
			it.close();
		}				
	}
	
	private void transformNotations(SKOSModel skosModel, OWLModel owlModel, ARTURIResource concept) throws ModelAccessException, ModelUpdateException {
		ARTURIResource currentClass = transformURI(concept);
		ARTLiteralIterator it = skosModel.listNotations(concept);
		try {
			while (it.streamOpen()) {
				ARTLiteral label = it.getNext();
				owlModel.addComment(currentClass, label.getLabel(), label.getLanguage(), NodeFilters.MAINGRAPH);
			}
		} finally {
			it.close();
		}		
	}


	private void transformAltLabels(SKOSModel skosModel, OWLModel owlModel, ARTURIResource concept) throws ModelUpdateException, ModelAccessException {
		ARTURIResource currentClass = transformURI(concept);
		ARTLiteralIterator it = skosModel.listAltLabels(concept, true);
		try {
			while (it.streamOpen()) {
				ARTLiteral label = it.getNext();
				if (label != null) 
				owlModel.addLabel(currentClass, label.getLabel(), label.getLanguage(), NodeFilters.MAINGRAPH);
			}
		} finally {
			it.close();
		}		
	}

	private Pattern p = Pattern.compile("^(\\w(?:\\.\\d+)+)(.+)");

	private void transformPrefLabels(SKOSModel skosModel, OWLModel owlModel, ARTURIResource concept) throws ModelUpdateException, ModelAccessException {
		ARTURIResource currentClass = transformURI(concept);
		ARTLiteralIterator it = skosModel.listPrefLabels(concept, true);
		try {
			while (it.streamOpen()) {
				ARTLiteral label = it.getNext();
				String lexicalForm = label.getLabel();
				String lang = label.getLanguage();
				Matcher m = p.matcher(lexicalForm);
				
				if (m.find()) {
					String notation = m.group(1);
					lexicalForm = m.group(2).trim();

					owlModel.addTriple(currentClass, RDFS.Res.COMMENT, owlModel.createLiteral(notation, XmlSchema.STRING), NodeFilters.MAINGRAPH);
				}
				
				owlModel.addLabel(currentClass, lexicalForm, lang, NodeFilters.MAINGRAPH);
			}
		} finally {
			it.close();
		}		
	}

	private void transformRelated(SKOSModel skosModel, OWLModel owlModel, ARTURIResource concept) throws ModelAccessException, ModelUpdateException {
		ARTURIResource currentClass = transformURI(concept);
		ARTURIResourceIterator it = skosModel.listRelatedConcepts(concept, true);
		try {
			while (it.streamOpen()) {
				ARTURIResource broaderConcept = it.getNext();
				ARTURIResource superClassURI = transformURI(broaderConcept);
				owlModel.addTriple(currentClass, RDFS.Res.SEEALSO, superClassURI, NodeFilters.MAINGRAPH);
			}
		} finally {
			it.close();
		}
		
	}

	private void transformBroaders(SKOSModel skosModel, OWLModel owlModel, ARTURIResource concept) throws ModelAccessException, ModelUpdateException {
		ARTURIResource currentClass = transformURI(concept);
		ARTURIResourceIterator it = skosModel.listBroaderConcepts(concept, false, true);
		try {
			while (it.streamOpen()) {
				ARTURIResource broaderConcept = it.getNext();
				ARTURIResource superClassURI = transformURI(broaderConcept);
				owlModel.addSuperClass(currentClass, superClassURI, NodeFilters.MAINGRAPH);
			}
		} finally {
			it.close();
		}
	}
	
	private ARTURIResource transformURI(ARTURIResource concept) {
		return uriTransformer.transform(concept);
	}
}
